﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Xunit;
using YAPL.Language.Ast;
using YAPL.Language.Ast.Utils;

namespace YAPL.Test.YAPL.Language.Parser.Antlr
{
	public class AntlrInputParserTest
	{

		private readonly DefaultAstComparer<DefaultAstWalker> _comparer =
			new DefaultAstComparer<DefaultAstWalker>();

		[Fact]
		public void EmptyProgramShouldBeParsedCorrectly()
		{
			AssertThatProgramsAreEqual(FixturePath.EmptyProgram_xml, FixturePath.EmptyProgram_yapl);
		}

		[Fact]
		public void HelloWorldProgramShouldBeParsedCorrectly()
		{
			AssertThatProgramsAreEqual(FixturePath.HelloWorld_xml, FixturePath.HelloWorld_yapl);
		}

		[Fact]
		public void FunctionCallNoParamsProgramShouldBeParsedCorrectly()
		{
			AssertThatProgramsAreEqual(FixturePath.FunctionCallNoParams_xml, FixturePath.FunctionCallNoParams_yapl);
		}

		[Fact]
		public void AssignmentAndVariablesProgramShouldBeParsedCorrectly()
		{
			AssertThatProgramsAreEqual(FixturePath.AssignmentAndVariables_xml, FixturePath.AssignmentAndVariables_yapl);
		}

		[Fact]
		public void SimpleTypesProgramShouldBeParsedCorrectly()
		{
			AssertThatProgramsAreEqual(FixturePath.SimpleTypes_xml, FixturePath.SimpleTypes_yapl);
		}

		[Fact]
		public void SimpleMathProgramShouldBeParsedCorrectly()
		{
			AssertThatProgramsAreEqual(FixturePath.SimpleMath_xml, FixturePath.SimpleMath_yapl);
		}

		[Fact]
		public void UnaryOperatorsProgramShouldBeParsedCorrectly()
		{
			AssertThatProgramsAreEqual(FixturePath.UnaryOperators_xml, FixturePath.UnaryOperators_yapl);
		}

		[Fact]
		public void NestedFunctionCallProgramShouldBeParsedCorrectly()
		{
			AssertThatProgramsAreEqual(FixturePath.NestedFunctionCall_xml, FixturePath.NestedFunctionCall_yapl);
		}

		[Fact]
		public void PrintProgramShouldBeParsedCorrectly()
		{
			AssertThatProgramsAreEqual(FixturePath.Print_xml, FixturePath.Print_yapl);
		}

		[Fact]
		public void FunctionWithParamsProgramShouldBeParsedCorrectly()
		{
			AssertThatProgramsAreEqual(FixturePath.FunctionWithParams_xml, FixturePath.FunctionWithParams_yapl);
		}

		[Fact]
		public void VectorLengthProgramShouldBeParsedCorrectly()
		{
			Expression exprYapl = null;
			Assert.DoesNotThrow(() => exprYapl = TestHelper.GetAstFromYAPL(FixturePath.VectorLength_yapl));
			Assert.NotNull(exprYapl);
			Assert.IsType(typeof(ProgramDefExpression), exprYapl);
			var programDef = (ProgramDefExpression)exprYapl;

			Assert.Equal(10, programDef.ProgramBody.Expressions.Count);
		}

		[Fact]
		public void TypeConversionProgramShouldBeParsedCorrectly()
		{
			Expression exprYapl = null;
			Assert.DoesNotThrow(() => exprYapl = TestHelper.GetAstFromYAPL(FixturePath.TypeConversion_yapl));
			Assert.NotNull(exprYapl);
			Assert.IsType(typeof(ProgramDefExpression), exprYapl);
			var programDef = (ProgramDefExpression)exprYapl;

			Assert.Equal(4, programDef.ProgramBody.Expressions.Count);
			var bodyExprs = programDef.ProgramBody.Expressions;
			Assert.IsType(typeof(AssignmentExpression), bodyExprs[0]);
			Assert.IsType(typeof(FunctionCallExpression), bodyExprs[1]);
			Assert.IsType(typeof(FunctionCallExpression), bodyExprs[2]);
			Assert.IsType(typeof(FunctionCallExpression), bodyExprs[3]);
		}


		[Fact]
		public void ArithmeticProgramShouldBeParsedCorrectly()
		{
			Expression exprYapl = null;
			Assert.DoesNotThrow(() => exprYapl = TestHelper.GetAstFromYAPL(FixturePath.Arithmetic_yapl));
			Assert.NotNull(exprYapl);
			Assert.IsType(typeof(ProgramDefExpression), exprYapl);
			var programDef = (ProgramDefExpression)exprYapl;

			Assert.Equal(6, programDef.ProgramBody.Expressions.Count);
			var bodyExprs = programDef.ProgramBody.Expressions;
			Assert.IsType(typeof(AssignmentExpression), bodyExprs[0]);
			Assert.IsType(typeof(AssignmentExpression), bodyExprs[1]);
			Assert.IsType(typeof(AssignmentExpression), bodyExprs[2]);
			Assert.IsType(typeof(AssignmentExpression), bodyExprs[3]);
			Assert.IsType(typeof(AssignmentExpression), bodyExprs[4]);
			Assert.IsType(typeof(FunctionCallExpression), bodyExprs[5]);
		}

		[Fact]
		public void CallingMethodOnSimpleTypeProgramShouldBeParsedCorrectly()
		{
			AssertThatProgramsAreEqual(FixturePath.CallingMethodOnSimpleType_xml, FixturePath.CallingMethodOnSimpleType_yapl);
		}

		[Fact]
		public void FunctionInsideCodeBlockProgramShouldBeParsedCorrectly()
		{
			AssertThatProgramsAreEqual(FixturePath.FunctionInsideCodeBlock_xml, FixturePath.FunctionInsideCodeBlock_yapl);
		}

		[Fact]
		public void SimpleClassProgramShouldBeParsedCorrectly()
		{
			AssertThatProgramsAreEqual(FixturePath.SimpleClass_xml, FixturePath.SimpleClass_yapl);
		}

		[Fact]
		public void ClassInitializerProgramShouldBeParsedCorrectly() {
			AssertThatProgramsAreEqual(FixturePath.ClassInitializer_xml, FixturePath.ClassInitializer_yapl);
		}

		[Fact]
		public void BooleanUsageProgramShouldBeParsedCorrectly() {
			AssertThatProgramsAreEqual(FixturePath.BooleanUsage_xml, FixturePath.BooleanUsage_yapl);
		}

		[Fact]
		public void SimpleIfProgramShouldBeParsedCorrectly() {
			AssertThatProgramsAreEqual(FixturePath.SimpleIf_xml, FixturePath.SimpleIf_yapl);
		}

		[Fact]
		public void SimpleIfElseProgramShouldBeParsedCorrectly() {
			AssertThatProgramsAreEqual(FixturePath.SimpleIfElse_xml, FixturePath.SimpleIfElse_yapl);
		}

		[Fact]
		public void WhileProgramShouldBeParsedCorrectly() {
			AssertThatProgramsAreEqual(FixturePath.While_xml, FixturePath.While_yapl);
		}

		#region helpers
		private void AssertThatProgramsAreEqual(string xmlProgramPath, string yaplProgramPath)
		{
			var exprXml = TestHelper.GetAstFromXML(xmlProgramPath);
			var exprYapl = TestHelper.GetAstFromYAPL(yaplProgramPath);
			Assert.True(_comparer.Compare(exprXml, exprYapl));
		}
		#endregion
	}
}
